Skip to content

feat(ledger): audit FAILURE and DENIED outcomes for ledger writes - #137

Merged
tiana-code merged 3 commits into
mainfrom
feat/E-01-audit-failure-denied
Jun 15, 2026
Merged

feat(ledger): audit FAILURE and DENIED outcomes for ledger writes#137
tiana-code merged 3 commits into
mainfrom
feat/E-01-audit-failure-denied

Conversation

@tiana-code

Copy link
Copy Markdown
Owner

What

Additive continuation of #49 (SUCCESS-path audit). Records the two outcomes #49 deferred:

  • FAILURE: a state-changing ledger request rejected by business logic (double-entry, currency, already-reversed, concurrency, duplicate, domain, reverse-target not-found, idempotency-conflict) writes one audit_events row with result=FAILURE from GlobalExceptionHandler in a REQUIRES_NEW transaction, so it survives the business rollback.
  • DENIED: an authenticated caller forbidden on a write endpoint (HTTP 403) writes one result=DENIED row from a Spring Security AccessDeniedHandler.

The SUCCESS path from #49 is unchanged. The audit write is observability only: a failing audit write is swallowed-and-logged and never converts a 4xx/403 into a 500.

How

Tests

Unit: resolver, recorder (allowlist + hash + swallow-log), access-denied handler (DENIED + 403 + swallow-log), recordOutcome. Integration: FailureAuditIT (unbalanced post -> committed FAILURE, reverse-of-unknown, validation-400 not audited), DeniedAuditIT (403 -> DENIED, 401 not audited). No migration, no entity change.

Closes #133

Record a committed FAILURE audit row from the global exception handler in a
REQUIRES_NEW transaction so it survives the business rollback, and a DENIED
row from a Spring Security AccessDeniedHandler. The SUCCESS path from #49 is
unchanged. The audit write is observability only and never masks the client
4xx/403 response.

Closes #133
} catch (
@Suppress("TooGenericExceptionCaught") ex: RuntimeException,
) {
log.warn("audit DENIED write failed for {} {}", request.method, request.requestURI, ex)
} catch (
@Suppress("TooGenericExceptionCaught") ex: RuntimeException,
) {
log.warn("audit FAILURE write failed for {} {}", request.method, request.requestURI, ex)
@tanya_r added 2 commits June 15, 2026 18:53
FailureAuditIT requests now send a bearer token (were 401), and both ITs use
UUID correlation ids so CorrelationIdFilter honors the inbound header (it
canonicalizes only valid UUIDs), making the per-test row filter match.
@tiana-code
tiana-code merged commit 8e3f21d into main Jun 15, 2026
6 checks passed
@tiana-code
tiana-code deleted the feat/E-01-audit-failure-denied branch June 15, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit FAILURE/DENIED outcomes for ledger writes

2 participants